/*Copyright ©2016 TommyLemon(https://github.com/TommyLemon/APIJSON)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.*/

package com.xlongwei.light4j.apijson;

import java.sql.Connection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import com.xlongwei.light4j.util.MySqlUtil;

import apijson.Log;
import apijson.framework.APIJSONSQLExecutor;
import apijson.orm.SQLConfig;
import lombok.extern.slf4j.Slf4j;


/**executor for query(read) or update(write) MySQL database
 * @author Lemon
 */
@Slf4j
public class DemoSQLExecutor extends APIJSONSQLExecutor {
	public static final String TAG = "DemoSQLExecutor";

	/** 用于判断哪个请求会有连接未关闭 */
	public static final AtomicInteger borrowedConnections = new AtomicInteger(0);
	/**
	 * apijson基本上是单线程的，因此可以使用ThreadLocal，如果有较多多线程，那么就更难跟踪已打开的数据库连接了
	 */
	public static final ThreadLocal<Connection> threadConnection = ThreadLocal.withInitial(() -> {
		try{
			Connection conn = MySqlUtil.DATASOURCE.getConnection();
			Log.v(TAG, "connection borrowed to {}"+borrowedConnections.incrementAndGet());
			return conn;
		}catch(Exception e) {
			log.info("{} {}", e.getClass().getSimpleName(), e.getMessage());
			return null;
		}
	});
	
    @Override
	public Connection getConnection(SQLConfig config) throws Exception {
		Log.d(TAG, "getConnection  config.getDatasource() = " + config.getDatasource());

		String connectionKey = config.getDatasource() + "-" + config.getDatabase();
		Connection c = connectionMap.get(connectionKey);
		if (c == null || c.isClosed()) {
			try {
				connectionMap.put(connectionKey, threadConnection.get());
			} catch (Exception e) {
				Log.e(TAG, "getConnection   try { "
						+ "DataSource ds = DemoApplication.getApplicationContext().getBean(DataSource.class); .."
						+ "} catch (Exception e) = " + e.getMessage());
			}
		}

		// 必须最后执行 super 方法，因为里面还有事务相关处理。
		// 如果这里是 return c，则会导致 增删改 多个对象时只有第一个会 commit，即只有第一个对象成功插入数据库表
		return super.getConnection(config);
	}
    
	@Override
	public synchronized void close() {
		if(connectionMap!=null && !connectionMap.isEmpty()) {
			Set<Connection> conns = new HashSet<>(connectionMap.values());
			Connection conn = threadConnection.get();
			conns.add(conn);
			try {
				if(conns.size() > 1) {
					log.error("apijson connection leak, conns={}", conns.size());
				}
				for(Connection con : conns) {
					if(!con.isClosed()) {
						con.close();
						borrowedConnections.decrementAndGet();
					}
				}
				if(conns.size() > 1) {
					log.info("apijson connection return to {}", conns.size());
				}else {
					Log.v(TAG, "connection returned to {}"+borrowedConnections.get());
				}
			}catch(Exception e) {
				log.info("{} {}", e.getClass().getSimpleName(), e.getMessage());
			}
			threadConnection.remove();
			connectionMap.clear();
		}
		if(cacheMap != null) {
			super.close();
		}
	}
}
